#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/platform.H>
#include <FL/Fl_Menu_.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Menu_Button.H>
#include <FL/fl_message.H>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
#include <unordered_map>
#include <windows.h>
#include <iphlpapi.h>
#include <Ipifcons.h>

using namespace std;

// linklib fltk Iphlpapi
// vc-extra-compile-flags: /DWIN32 /MD /source-charset:utf-8
// vc-extra-link-flags: /SUBSYSTEM:WINDOWS
// mingw-extra-compile-flags: -DWIN32
// mingw-extra-link-flags: -mwindows -lole32 -luuid -lcomctl32

struct NicInfo {
    DWORD outOctets; // 上传总量(单位：字节)
    DWORD inOctets; // 下载总量(单位：字节)
};

// 通过右键菜单选择了哪块网卡
string which_nic;

// 上次搜集到的关于各个网卡的信息
std::unordered_map<string, NicInfo> old_nic_info_list;
// 本次搜集到的关于各个网卡的信息
std::unordered_map<string, NicInfo> nic_info_list;

// 处理右键菜单各菜单项
static void Menu_CB(Fl_Widget*,void* data)
{
    Fl_Menu_Button* right_menu = (Fl_Menu_Button*)data;
    // DETERMINE WHICH ITEM USER PICKED
    const char *text = right_menu->text();     // get label of item user picked
    if (!text)
    	return;
    if (strcmp(text, "Quit") == 0 ) {
        exit(0);
    }

    // 记住用户通过右键菜单选择了哪块网卡
	which_nic = text;

	Fl_Menu_Item* menu_item = (Fl_Menu_Item*)right_menu->mvalue();
	menu_item->setonly();

	// fl_message("You chose to do a thing");
}

// 用于跟踪拖动窗口时的鼠标位置
int start_x = 0;
int start_y = 0;
bool is_dragging = false;

Fl_Menu_Button* right_menu_btn;

class MonitorWindow : public Fl_Window {
public:
    MonitorWindow(int w, int h, const char* title = 0)
        : Fl_Window(w, h, title)
        , box(0, 0, w, h, "Calculating...")
        , right_menu(0, 0, w, h) // 右键菜单按钮覆盖整个主窗口

    {
        // border(0); // 隐藏标题栏(一旦隐藏标题栏，就无法成为topmost窗口)

        // resizable(this);

		// 不要标题栏
		clear_border();

        right_menu.type(Fl_Menu_Button::POPUP3);         // pops menu on right click
        right_menu.add("Quit",       "^q", Menu_CB, &right_menu);  // ctrl-q hotkey

        right_menu_btn = &right_menu;
    }

    // 更新显示的上传和下载速度
    // void updateSpeeds(double dwOutOctets, double dwInOctets)
    void updateSpeeds()
    {
	    cout << "update speed for: " << which_nic << "\n";
	    NicInfo nic_info = nic_info_list[which_nic];
	    NicInfo old_nic_info = old_nic_info_list[which_nic];

        // 第一次无增量，所以无法计算网速
        if (old_nic_info.outOctets == 0 && old_nic_info.inOctets == 0) {
	        box.copy_label("No network\nactivity");
		    cout << "return " << "\n";
            return;
        }

        double deltaOut = nic_info.outOctets - old_nic_info.outOctets;
        double deltaIn = nic_info.inOctets - old_nic_info.inOctets;

        std::stringstream ss;
        ss << std::fixed << std::setprecision(2) << u8"Upload: " << deltaOut / 1024
        	<< u8" KB/s\nDownload: " << deltaIn / 1024 << " KB/s";
        box.copy_label(ss.str().c_str());
    }

	// 实现鼠标拖动窗口
    int handle(int event) override
    {
        switch (event) {
        case FL_PUSH:
            if (Fl::event_button() == FL_LEFT_MOUSE) {
                start_x = Fl::event_x_root() - x();
                start_y = Fl::event_y_root() - y();
                is_dragging = true;
            }
            break;
        case FL_DRAG:
            if (is_dragging) {
                int new_x = Fl::event_x_root() - start_x;
                int new_y = Fl::event_y_root() - start_y;
                position(new_x, new_y);
            }
            break;
        case FL_RELEASE:
            is_dragging = false;
            break;
        default:
            break;
        }
        // return 0;
        // 不把事件交给基类的handle处理，窗口就无法弹出右键菜单
        return Fl_Window::handle(event);
    }

private:
    Fl_Box box;
    Fl_Menu_Button right_menu;
};

// 字符串中是否含有特定子串
bool str_contains(const string& mainStr, const string& subStr)
{
    size_t found = mainStr.find(subStr);
    return found != std::string::npos;
}

string add_backslash(const string& input)
{
    string result;
    for (char ch : input) {
        if (ch == '/' || ch == '&' || ch == '_') {
            result += '\\';
        }
        result += ch;
    }

    return result;
}

void callback(void* w)
{
	MonitorWindow* win = static_cast<MonitorWindow*>(w);

    DWORD dwSize = 0;
    GetIfTable(NULL, &dwSize, FALSE);

    MIB_IFTABLE* pIfTable = (MIB_IFTABLE*)malloc(dwSize);
    GetIfTable(pIfTable, &dwSize, FALSE);
    std::cout << "--------------dwNumEntries: " << pIfTable->dwNumEntries << "\n";

	right_menu_btn->clear();
	old_nic_info_list = nic_info_list;
	nic_info_list.clear();

    for (DWORD i = 0; i < pIfTable->dwNumEntries; i++) {
        MIB_IFROW ifRow = pIfTable->table[i];

        if (ifRow.dwType != IF_TYPE_ETHERNET_CSMACD && ifRow.dwType != IF_TYPE_IEEE80211)
        // if (ifRow.dwType != IF_TYPE_IEEE80211)
        	continue;

	    if (ifRow.dwAdminStatus == 2) // 该网卡已禁用
	    	continue;

		string nic_name = (char*)ifRow.bDescr;
		if (str_contains(nic_name, "Filter")
			|| str_contains(nic_name, "Npcap")
			|| str_contains(nic_name, "Scheduler")
			|| str_contains(nic_name, "Virtual")
			|| str_contains(nic_name, "Miniport")
			|| str_contains(nic_name, "Bluetooth")
			|| str_contains(nic_name, "VPN")
			)
			continue;

		std::cout << ifRow.dwOperStatus << " ";
        // std::wcout << ifRow.wszName << " ";
        std::cout << ifRow.bDescr << " ";
        std::cout << ifRow.dwAdminStatus << " ";
        std::cout << ifRow.dwType << " " << ifRow.dwOutOctets
        	<< " " << ifRow.dwInOctets << "\n";

		// 重新构造右键菜单中的网卡列表
		// 原本菜单项为(char*)ifRow.bDescr
		// 但因为其中可能含有& / _ 等字符，这些字符对于add函数有特殊意义，需要用\转义
		string nic_name_escaped = add_backslash(nic_name);		
        right_menu_btn->add(nic_name_escaped.c_str(), 0, Menu_CB, right_menu_btn, FL_MENU_RADIO);

		// 重新搜集各网卡上传下载总量信息
		nic_info_list[nic_name] = {ifRow.dwOutOctets, ifRow.dwInOctets};

		// 在用户没有选择网卡的情况下，
		// 默认选中一个处于活动状态的网卡(如果有多个网卡都处在活动状态，将选中第一个处在活动状态的)
		if (which_nic.empty() && ifRow.dwOperStatus == IF_OPER_STATUS_OPERATIONAL) {
			which_nic = nic_name;
		}

    }


    free(pIfTable);

	// right_menu_btn->add("----");
	// FL_MENU_DIVIDER // Creates divider line below this item. Also ends a group of radio buttons.
    right_menu_btn->add("Quit", 0, Menu_CB, right_menu_btn);

    // 因为重建了右键菜单，所以要在菜单的radio上把当前网卡反映出来
    if (!which_nic.empty()) {
	    Fl_Menu_Item* menu_item = (Fl_Menu_Item*)right_menu_btn->find_item(which_nic.c_str());
	    menu_item->setonly();
    }

	// 更新悬浮窗
	win->updateSpeeds();

    Fl::repeat_timeout(1.0, callback, w);
}

int main()
{
	// 悬浮窗
    MonitorWindow win(160, 50, "Network Speed Monitor");

    // 让悬浮窗出现在屏幕右下角
    win.position(Fl::w() - win.w(), Fl::h() - win.h());

    Fl::add_timeout(1.0, callback, &win);

    win.end();
    win.show();

	// 让悬浮窗成为topmost窗口(即在所有其他窗口之前)
	// 必须在窗口显现之后获取句柄，不然得到的就是NULL
    HWND hwnd = fl_xid(&win);
    cout << "hwnd: " << hwnd << endl;
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

    return Fl::run();
}
